// Copyright 2007 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.enterprise.connector.afyd;

import com.google.enterprise.connector.spi.ConnectorType;
import com.google.enterprise.connector.spi.ConfigureResponse;

import com.google.gdata.client.appsforyourdomain.UserService;
import com.google.gdata.data.appsforyourdomain.provisioning.UserEntry;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;

import java.util.Map;
import java.util.ResourceBundle;
import java.util.Locale;
import java.util.logging.Logger;
import java.net.URL;
import java.net.MalformedURLException;
import java.io.IOException;

/**
 * This class serves as the ConnectorType for the Afyd connector family.
 * 
 * @author amsmith@google.com (Adam Smith)
 */
public class AfydConnectorType implements ConnectorType {

  /** The logger for this class. */
  private static final Logger LOGGER =
      Logger.getLogger(AfydConnectorType.class.getName());
  
  // This pattern is taken from public API docs here:
  // http://code.google.com/apis/apps/gdata_provisioning_api_v2.0_reference.html
  private static final String USER_FEED_PATTERN = 
    "https://www.google.com/a/feeds/{domain}/user/2.0/{userName}";

  // These public because they are visible in configuration form output.
  public static final String DOMAIN_KEY = "domain";
  public static final String EMAIL_KEY = "email";
  public static final String PASSWORD_KEY = "Password"; // encrypted on disk
  
  // these strings associate human readable labels for form fields
  private static final String DOMAIN_LABEL = "domain_label";
  private static final String EMAIL_LABEL = "email_label";
  private static final String PASSWORD_LABEL = "password_label";

  // these strings associate human readable verification errors.
  private static final String MUST_SUPPLY_DOMAIN_MSG = "must_supply_domain_msg";
  private static final String MUST_SUPPLY_EMAIL_MSG = "must_supply_email_msg";
  private static final String MUST_SUPPLY_PASS_MSG = "must_supply_pass_msg";
  private static final String BAD_EMAIL_PASS_MSG = "bad_email_pass_msg";
  private static final String BAD_DOMAIN_MSG = "bad_domain_msg";
  private static final String FAILED_CONTACTING_SERVICE_MSG = 
    "failed_contacting_service_msg";
  private static final String MUST_BE_ADMIN_MSG = "must_be_admin_msg";
  
  /** The service used to test login credentials */
  UserService service;
  
  /**
   * Construct a connector type using the given back end service.
   * @param service used to test login credentials
   */
  public AfydConnectorType(UserService service) {
    this.service = service;
  }
  
  /** {@inheritDoc} */
  public ConfigureResponse getConfigForm(Locale locale) {
    return new ConfigureResponse("", makeForm(null, locale));    
  }
  
  /** {@inheritDoc} */
  public ConfigureResponse getPopulatedConfigForm(Map configMap,
      Locale locale) {
    return new ConfigureResponse("", makeForm(configMap, locale));
  }
  
  /**
   * {@inheritDoc}
   */
  public ConfigureResponse validateConfig(Map configMap, Locale locale) {
    String problemMessage = getFirstProblemMessage(configMap, service);
    if (problemMessage == null) {
      return null;
    } else {
      return new ConfigureResponse(getString(problemMessage, locale),
                                   makeForm(configMap, locale));
    }
  }
  
  private static String makeForm(Map configMap, Locale locale) {
    String domain = null;
    String email = null;
    String password = null;
    
    if (configMap != null) {
      domain = (String) configMap.get(DOMAIN_KEY);
      email = (String) configMap.get(EMAIL_KEY);
      password = (String) configMap.get(PASSWORD_KEY);
    }
    
    if (domain == null)
      domain = "";
    if (email == null)
      email = "";
    if (password == null)
      password = "";
    
    StringBuffer buffer = new StringBuffer(256)
      .append(makeInputRow(
          getString(DOMAIN_LABEL, locale),
          DOMAIN_KEY,
          domain,
          15,
          false))
      .append(makeInputRow(
          getString(EMAIL_LABEL, locale),
          EMAIL_KEY,
          email,
          20,
          false))
      .append(makeInputRow(
          getString(PASSWORD_LABEL, locale),
          PASSWORD_KEY,
          password,
          20,
          true));

    return buffer.toString();
  }
  
  private static String getFirstProblemMessage
  (Map configMap, UserService service) {
    
    String domain = (String) configMap.get(DOMAIN_KEY);
    if (domain == null || domain.length() == 0)
      return MUST_SUPPLY_DOMAIN_MSG;
    
    String email = (String) configMap.get(EMAIL_KEY);
    if (email == null || email.length() == 0)
      return MUST_SUPPLY_EMAIL_MSG;
    
    String password = (String) configMap.get(PASSWORD_KEY);
    if  (password == null || password.length() == 0)
      return MUST_SUPPLY_PASS_MSG;
    
    try {
    service.setUserCredentials(email, password);
    } catch (AuthenticationException ae) {
      LOGGER.info(ae.toString());
      return BAD_EMAIL_PASS_MSG;
    }
    
    String username = email.substring(0, email.indexOf("@"));
    URL url = null;
    try {
      String urlString = USER_FEED_PATTERN
                            .replaceAll("\\{domain\\}", domain)
                            .replaceAll("\\{userName\\}", username);
      url = new URL(urlString);
    } catch (MalformedURLException murle) {
      LOGGER.info(murle.toString());
      return BAD_DOMAIN_MSG;
    }
    
    UserEntry userEntry = null;
    try {
      userEntry = (UserEntry) service.getEntry(url, UserEntry.class);
    } catch (ServiceException se) {
      LOGGER.info(se.toString());
      return FAILED_CONTACTING_SERVICE_MSG;
    } catch (IOException ioe) {
      LOGGER.info(ioe.toString());
      return FAILED_CONTACTING_SERVICE_MSG;
    }
  
    if (userEntry.getLogin().getAdmin().booleanValue() == false) {
      LOGGER.info("Given user is not an admin.");
      return MUST_BE_ADMIN_MSG;
    }
    
    return null;
  }
   
  /** Builds one row of of the form table. */
  private static String makeInputRow
    (String label, String key, String value, int size, boolean hidden) {
    
    StringBuffer buffer = new StringBuffer(256)
      .append("<tr>\n")
      .append("\t<td>")
      .append(label)
      .append("</td>\n")
      .append("\t<td>\n")
      .append("\t\t<input type=\"" + (hidden ? "password" : "text") + 
          "\" name=\"")
      .append(key)
      .append("\" size=\"")
      .append(size)
      .append("\" value=\"")
      .append(value)
      .append("\" />\n")
      .append("\t</td>\n")
      .append("</tr>\n");
    
    return buffer.toString();
  }
  
  /** Gets the localized string for a given key. */
  private static String getString(String key, Locale locale) {
    ResourceBundle rb = ResourceBundle.getBundle("AfydResources", locale);
    return rb.getString(key);
  }
}
